home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 1993 May / Info-Mac_II_May_1993.to_.sit / Info-Mac II (May 1993).toast / Unix / Digest Reader 2.0.txt < prev    next >
Internet Message Format  |  1992-08-29  |  50KB

  1. Date: Tue, 19 Nov 91 08:24:39 EST 
  2. From: dbbrown@eastrg2.cray.com (Dan Brown)
  3. Subject: [*] digest-reader.txt (Version 2) 
  4.  
  5.         Here is Version 2 of digest-reader.txt, that I submitted earlier this 
  6.         year. The changes were made by Michael Kimura (mnk@rdac.dnet.hac.com),
  7.         who uses the program alot and made to very useful improvements. Just
  8.         some of the major improvements include.
  9.  
  10.         o The program has been ported to work with VMS/VAXC as well as UNIX/C
  11.           and has been tested in both environments.
  12.  
  13.         o You now have the ability to extract individual articles from the
  14.           digest being read.
  15.  
  16.         o The article recognition algorithm has been generalized to accomodate
  17.           many other digests created by other interest groups with similar
  18.           formats. Some of the other digests tested are BIG-LAN DIGEST,
  19.           RISKS-LIST: RISKS-FORUM Digest, Telecom Privacy Digest,
  20.           TELECOM Digest.
  21.  
  22.     o ``dig'' now processes multiple arguments (digests) specified
  23.       on the command line (Works for VMS as well).
  24.  
  25.     o The article menu contains both the subject and the author 
  26.           (if it fits on the line).
  27.  
  28.     o Added code to display the percent remaining of the article (nn%) 
  29.           to the prompt while reading an article.
  30.  
  31.     o It seemed more natural for continus pressing of return to take you
  32.       through the current article and then to the next article
  33.       than having pressing return take you back to the article
  34.       menu.
  35.  
  36.     o Changed the  Previous and Next processing so that
  37.       Previous and Next will "wrap" around back to the beginning
  38.       or end in either the topic display or while displaying articles.
  39.  
  40.     o In conjunction with the change to having "ret" take you to
  41.       the next article instead of the article menu, I found I would
  42.       like to know what the next article's subject is.  So I
  43.       reduced the number of lines displayed by one so that the
  44.       next article's subject can be displayed just before the prompt
  45.       when the current article has been completely displayed.  
  46.  
  47.         o Many other cosmetic changes.
  48.  
  49.       I just want to thank Mike for taking the time to improve on the program. 
  50. I hope everyone who uses this program will appreciate the enhancements made by
  51. Mike. 
  52.  
  53.                         Enjoy,
  54.                           Dan Brown
  55.  
  56. ---------------------------------< digest-reader.txt Follows >---------------
  57. #include <stdio.h>
  58. #include <string.h>
  59. #include <ctype.h>
  60. #include <stdlib.h>
  61.  
  62. #if VAXC
  63. #include rmsdef        /* RMS DEFinitions (status codes for LIB$FIND_FILE) */
  64. #endif
  65.  
  66. /*
  67.  - Compile line: cc dig.c -o dig
  68.  -
  69.  - Command line: dig [digest]
  70.  -
  71.  - Written by: Dan Brown (dbbrown@eastrg2.cray.com)
  72.  -
  73.  - Modified by: Michael Kimura (mnk@rdac.dnet.hac.com) 10/11/91
  74.  -
  75.  -    o Made program capable of reading most digests (used to read
  76.  -        only Info-Mac consistently)  Had to change beginning of article
  77.  -      logic to look for Hyphens instead of "Date:" (not all digests
  78.  -      have "Date:" as first line of each article).
  79.  -
  80.  -    o Added extract command to extract the article being displayed
  81.  -        to a file.
  82.  -
  83.  -    o Made first article (which is the digest header) article 0 instead
  84.  -      of article 1 and changed the total number of articles to not
  85.  -        include article 0.  Program now parses either the digest title
  86.  -        line or the first Subject: line to determine the digest name,
  87.  -      volume number, and issue number and displays this information
  88.  -        in the prompt line.
  89.  -
  90.  -    o Increased MAX_ARTICLES from 75 to 100 (i.e. 99 articles plus
  91.  -      article 0) and added boundary checking to make sure a digest
  92.  -      with more than 99 articles doesn't crash this program.  A
  93.  -      warning message is generated for digests with >99 articles.
  94.  -
  95.  -    o Made VAX C compatible.
  96.  -        - fseek() in VAX C works fine for stream files but most
  97.  -          other files you can only seek to locations returned by
  98.  -          ftell().  Doing arithmetic on offsets returned by ftell()
  99.  -           in VAX C produces incorrect results.  Modified the code
  100.  -              to call ftell() *before* reading the next record.
  101.  -
  102.  -        - "lines" conflicts with VAX C run-time library, changed
  103.  -          "lines" to "xlines"
  104.  -
  105.  -        - VAX C run-time library does not contain the routine
  106.  -          "bzero()" so added "bzero()" to this file.
  107.  -
  108.  -        - VAX C I/O eats a totally blank line output after user
  109.  -          input to a prompt, added logic to output a line with a
  110.  -          space to force out blank line after user input if VAX C.
  111.  -
  112.  - Modified by: Michael Kimura (mnk@rdac.dnet.hac.com) 11/12/91
  113.  -
  114.  -    o Added code to process all arguments as digest names.  Also
  115.  -      added code so that under VAXC it will process wildcard
  116.  -      specifications in arguments.
  117.  -
  118.  -      o Added code to parse the "personal name" out of the From: line
  119.  -        and to append " (personal name)" to the subject.
  120.  -
  121.  -      o Changed call from bzero() to memset() since memset() is in
  122.  -        the VAX C run-time library.
  123.  -
  124.  -      o Added percent remaining of the article (nn%) to the prompt
  125.  -        while reading an article.
  126.  -
  127.  -      o Changed Previous and Next processing so that Previous and
  128.  -        Next will "wrap" around back to the beginning or end in
  129.  -        either the topic display or while displaying articles.  Hence,
  130.  -        after displaying the last article, Next will take you to
  131.  -        article 0.  Also changed "ret" to be "Next" instead of
  132.  -        "return to menu".  This way pressing "ret" while displaying
  133.  -        articles will take you through all of the articles.
  134.  -
  135.  -      o Reduced the number of lines displayed by one so that the
  136.  -        next article's subject is displayed just before the prompt
  137.  -        when the current article has been completely displayed.
  138.  */
  139.  
  140. /* Pre-Processor Definitions Section */
  141.  
  142. #define  MAX_FILE_LEN   81      /* Maximum file name length - 1 */
  143. #define  MAX_ARTICLES  100      /* Maximum number of articles */
  144. #define  MAX_ANS         5      /* Maximum answer size */
  145. #define  LINE_LEN       81      /* Maximum Subject/From line len - 1 */
  146. #define  PROMPT_LINES    3      /* Number of menu prompt lines */
  147. #define  COLS           81      /* Number columns on the screen */
  148.  
  149. #define  OUTPUT          0      /* Output indicator */
  150. #define  INPUT           1      /* Input indicator */
  151.  
  152. /* 
  153. -- Minimum number of working lines enough for at least 1 article, 
  154. -- and menu prompt.
  155. */
  156.  
  157. #define  MIN_LINES       (PROMPT_LINES + 1)      
  158.  
  159.     /* Line identification values */
  160. #define  DIGEST_TITLE    0      /* Digest Title line */
  161. #define  SUBJECT         1      /* Subject line */
  162. #define  FROM            2      /* From line */
  163. #define  HYPHENS         3      /* Line with leading hyphens */
  164. #define  UNKNOWN         4      /* Unknown line type */
  165.  
  166.     /* Menu Selections */
  167. #define  QUIT            -1     /* QUIT menu selection */
  168. #define  HELP            -2     /* HELP menu selection */
  169. #define  NEXT            -3     /* NEXT menu selection */
  170. #define  PREVIOUS        -4     /* PREVIOUS menu selection */
  171. #define  ILLEAGAL        -5     /* ILLEAGAL user reponse */
  172. #define  FIRST_PAGE      -6     /* Goto the first menu page */
  173.  
  174.     /* Screen clear macro */
  175.  
  176. #if !VAXC
  177. #define  clear()        (void) system("clear")
  178. #else
  179. #define  clear()        ;
  180. /* This can be used on VMS but WARNING, it spawns a subprocess so */
  181. /* it's can be very slow */
  182. /*
  183. #define  clear()    (void) system("type/page nl:")
  184. */
  185. #endif
  186.  
  187. /* Uncomment this and comment above if necessary */
  188. /*
  189. #define  clear()
  190. */
  191.  
  192. /* External definitions section */
  193.  
  194. extern char *memset();
  195.  
  196. /* Global variables Section */
  197.  
  198.     /* Program ident string */
  199. static char id[] = "@(#)dig v2.0 11/12/91 dbbrown@eastrg2.cray.com/mnk@rdac.dnet.hac.com"; 
  200.  
  201. char        errmsg[256];            /* Error message storage */
  202. char        digest[LINE_LEN];       /* Digest name */
  203. int         volume;                 /* Volume number */
  204. int         issue;                  /* Issue number */
  205. int         xlines = 24;            /* Number of lines on screen */
  206.  
  207. /* Selected info extracted from the given digest */
  208.  
  209. typedef struct {
  210.     long   offset;            /* Offset to begining of article */
  211.     int    num_lines;         /* Number of lines in the article */
  212.     char   subject[LINE_LEN]; /* Extracted "Subject" from article */
  213.     char   from[LINE_LEN];    /* Extracted "From" from article */
  214. }   article;
  215.  
  216. main(argc, argv)
  217. int    argc;
  218. char   **argv;
  219. {
  220.     char      fname[MAX_FILE_LEN];  /* input file name */
  221.     int       result;               /* Return value of some functions */
  222.     char      *cptr;
  223.     void      process_digest();     /* Process the given digest */
  224.     int       arg;                  /* argument index */
  225.  
  226. #if VAXC
  227.     unsigned long   find_file();    /* Interface routine to LIB$FIND_FILE */
  228.     unsigned long   succ;        /* Result code from find_file() */
  229. #endif
  230.  
  231.     /* 
  232.     -- Determine the number of lines on the screen. If the environment 
  233.     -- variable LINES is not defined then the program will default to 
  234.     -- it's initial value.
  235.     */ 
  236.  
  237.     cptr = getenv("LINES");
  238.     if (cptr != (char *) NULL)
  239.     {
  240.         /* 
  241.         -- A LINES environment variable was found. Convert the 
  242.         -- returned string to an integer value.
  243.         */
  244.  
  245.         result = atoi(cptr);
  246.  
  247.         /* 
  248.         -- Determine if the value of the LINES variable has an 
  249.         -- integer value greater than MIN_LINES. 0 indicates either the 
  250.         -- value of LINES is 0 or it is a non-integer value. 
  251.         */
  252.  
  253.         if (result >= MIN_LINES)
  254.         {
  255.             /* The variable has a valid integer */
  256.  
  257.              xlines = result;
  258.         }
  259.     }
  260.  
  261.     /* Determine the input file name */
  262.  
  263.     if (argc == 1)
  264.     {
  265.         /* 
  266.         -- No file name was given on the command line. Get the filename 
  267.         -- from the users.
  268.         */
  269.  
  270.         result = getfilename(fname,INPUT);
  271.  
  272.         /* Determine if the user selected quit */
  273.  
  274.         if (result == -1)
  275.         {
  276.             /* The user elected to quit the program */
  277.  
  278.             clear();
  279.             (void) printf("\n\tExiting (%s).\n\n", argv[0]);
  280.             exit(0);
  281.         }
  282.  
  283.         /* Process the digest */
  284.  
  285.         process_digest(fname);
  286.     }
  287.     else
  288.     {
  289.         /* 
  290.         -- Take the rest of the arguments as file names and 
  291.         -- process them
  292.         */
  293.  
  294.     for (arg = 1; arg < argc; arg++)
  295.     {
  296. #if !VAXC
  297.             /* Process the digest(s) */
  298.  
  299.             process_digest(argv[arg]);
  300. #else
  301.         do
  302.         {
  303.         succ = find_file(argv[arg],fname,sizeof(fname));
  304.         if (succ != RMS$_NMF)
  305.                     process_digest(fname);
  306.         } while (succ == RMS$_NORMAL);
  307. #endif
  308.     }
  309.     }
  310. }
  311.  
  312. /* Process the given digest */
  313.  
  314. void process_digest(fname)
  315. char    *fname;                     /* input file name */
  316. {
  317.     FILE    *fp;                    /* Input file pointer */
  318.     int     result;                 /* Result returned by functions */
  319.     article articles[MAX_ARTICLES]; /* Article info from digest */
  320.     int     num_articles;           /* Total number of articles */
  321.     int     start;                  /* Index to first display article */
  322.     int     max_articles;           /* Maximum articles in menu */
  323.     int     cur_articles;           /* Number of articles to display */
  324.     int     last_article;           /* Pointer to last article */
  325.     int     done;                   /* Loop control variable */
  326.     void    display_article();
  327.  
  328.     /* Initialize local variables */
  329.  
  330.     memset((char *) articles, 0, sizeof(articles));
  331.  
  332.     /* Attempt to open the digest */
  333.  
  334.     fp = fopen(fname, "r");
  335.  
  336.     /* Determine if there was an error opening the file */
  337.  
  338.     if (fp == (FILE *) NULL)
  339.     {
  340.         /* 
  341.         -- There was an error opening the digest file, report the 
  342.         -- error and exit the function.
  343.         */
  344.  
  345.         (void) sprintf(errmsg, "%s (%d): Error opening file '%s'", 
  346.             __FILE__, __LINE__, fname);
  347.         (void) perror(errmsg);
  348.     }
  349.     else
  350.     {
  351.         /* The digest file was successfully opened. */
  352.  
  353.         (void) strcpy(digest,"Digest");
  354.         volume = 0;
  355.         issue = 0;
  356.  
  357.         /* Get the file's article information */
  358.  
  359.         result = get_art_info(fp, fname, articles, &num_articles);
  360.  
  361.         /* Determine if the function was successful */
  362.  
  363.         if (result == 0)
  364.         {
  365.             /* The function was successful */
  366.     
  367.             /* 
  368.             -- Set the initial values for the starting article and 
  369.             -- number of articles to display on the menu to list in 
  370.             -- the menu.
  371.             */
  372.  
  373.             start = 0;
  374.             last_article = num_articles - 1;   /* Zero based array */
  375.             max_articles = xlines - PROMPT_LINES;
  376.             if ((start + max_articles - 1) > last_article)
  377.             {
  378.                 /* 
  379.                 -- There are fewer articles to display than the 
  380.                 -- maximum number displayable. Calculate the actual 
  381.                 -- number of articles available.
  382.                 */
  383.  
  384.                 cur_articles = num_articles - start;
  385.             }
  386.             else
  387.             {
  388.                 /* We can safely display the max number of articles. */
  389.  
  390.                 cur_articles = max_articles;
  391.             }
  392.    
  393.             /* Do user actions until the user selects quit */
  394.  
  395.             done = 0;
  396.             while (!done)
  397.             {
  398.                 /* Display the menu of selections */
  399.  
  400.                 result = display_menu(articles, start, cur_articles,
  401.                          num_articles);
  402.  
  403.                 /* execute the users request */
  404.  
  405.                 switch(result)
  406.                 {
  407.                     case FIRST_PAGE:
  408.                         start = 0;
  409.  
  410.                         /* 
  411.                         -- Determine if there are fewer articles 
  412.                         -- available than can be maximumly displayed.
  413.                         */
  414.  
  415.                         if ((start + max_articles - 1) > last_article)
  416.                         {
  417.                             /* 
  418.                             -- There are fewer articles to display than 
  419.                             -- the maximum number displayable. 
  420.                             -- Calculate the actual number of articles 
  421.                             -- available.
  422.                             */
  423.  
  424.                             cur_articles = num_articles - start;
  425.                         }
  426.                         else
  427.                         {
  428.                             /* 
  429.                             -- We can safely display the maximum 
  430.                             -- number of articles.
  431.                             */
  432.  
  433.                             cur_articles = max_articles;
  434.                         }
  435.                         break;
  436.  
  437.                     case NEXT:     /* Next page of Articles */
  438.                         if (start + max_articles <= last_article)
  439.                         {
  440.                             start += max_articles;
  441.  
  442.                             /* 
  443.                             -- Determine if there are fewer articles 
  444.                             -- available than can be maximumly 
  445.                             -- displayed.
  446.                             */
  447.  
  448.                             if ((start + max_articles - 1) > 
  449.                                 last_article)
  450.                             {
  451.                                 /* 
  452.                                 -- There are fewer articles to display 
  453.                                 -- than the maximum number displayable. 
  454.                                 -- Calculate the actual number of 
  455.                                 -- articles available.
  456.                                 */
  457.  
  458.                                 cur_articles = num_articles - start;
  459.                             }
  460.                             else
  461.                             {
  462.                                 /* 
  463.                                 -- We can safely display the maximum 
  464.                                 -- number of articles.
  465.                                 */
  466.  
  467.                                 cur_articles = max_articles;
  468.                             }
  469.                         }
  470.                         break;
  471.  
  472.                     case PREVIOUS: /* Previous page of Articles */
  473.  
  474.                         /* The user selected previous page */
  475.  
  476.                         start -= max_articles;
  477.                         if (start < 0)
  478.                             start = last_article - 
  479.                                 (last_article % max_articles);
  480.  
  481.                         /* 
  482.                         -- Determine if there are fewer articles 
  483.                         -- available than can be maximumly displayed.
  484.                         */
  485.  
  486.                         if ((start + max_articles - 1) > last_article)
  487.                         {
  488.                             /* 
  489.                             -- There are fewer articles to display than 
  490.                             -- the maximum number displayable. 
  491.                             -- Calculate the actual number of articles 
  492.                             -- available.
  493.                             */
  494.  
  495.                             cur_articles = num_articles - start;
  496.                         }
  497.                         else
  498.                         {
  499.                             /* 
  500.                             -- We can safely display the maximum 
  501.                             -- number of articles.
  502.                             */
  503.  
  504.                             cur_articles = max_articles;
  505.                         }
  506.                         break;
  507.  
  508.                     case QUIT:     /* Quit the program */
  509.                         clear();
  510.                         done = 1;
  511.                         break;
  512.  
  513.                     case ILLEAGAL:
  514.                         break;
  515.  
  516.                     default:
  517.                         /* Test for article number here */
  518.  
  519.                         if (result > num_articles - 1)
  520.                         {
  521.                             clear();
  522.                             (void) printf(
  523.                                 "Invalid article number %d\n", result);
  524.                             sleep(1);
  525.                         }
  526.                         else
  527.                         {
  528.                             /* Display the article */
  529.  
  530.                             display_article(fp, fname, result, 
  531.                                 articles, num_articles);
  532.                         }
  533.                         break;
  534.                 }
  535.             }
  536.         }
  537.    
  538.         /* Close the open file */
  539.  
  540.         (void) fclose(fp);
  541.     }
  542. }
  543.  
  544. /* 
  545. -- This functions extracts article location, num_lines, and Subject 
  546. -- and From lines from each article found in the given file.
  547. */
  548.  
  549. int get_art_info(fp, fname, articles, num_articles)
  550. FILE       *fp;             /* Input file pointer */
  551. char       *fname;          /* Digest file name */
  552. article    articles[];      /* an array of article information */
  553. int        *num_articles;   /* Total number of articles in digest */
  554. {
  555.     int    return_value;    /* Function return value */
  556.     int    art_count;       /* Article count */
  557.     int    first_art;       /* Set if it is the first article */
  558.     int    done;            /* Loop control variable */
  559.     int    total_lines;     /* number of lines in digest */
  560.     int    last_line;       /* Last line of previous article */
  561.     char   buf[BUFSIZ];     /* Input buffer */
  562.     char   *cptr;           /* ptr into the Input buffer */
  563.     char   *cptr2;          /* another ptr into the Input buffer */
  564.     int    len;             /* Length of string */
  565.     char   name[BUFSIZ];    /* Name from From: line */
  566.     void   get_name();      /* Routine to get personal name from From: line */
  567.  
  568.     /* Initialize local variables */
  569.  
  570.     return_value = 0;
  571.     art_count = 0;
  572.     done = 0;
  573.     total_lines = 0;
  574.     last_line = 0;
  575.     first_art = 1;
  576.  
  577.     /* Start from the beginning of the file */
  578.  
  579.     (void) fseek(fp, 0L, 0);
  580.  
  581.     /* Process each article found in the file */
  582.  
  583.     while (!done)
  584.     {
  585.         /* Get the next line in the file */
  586.  
  587.         if (fgets(buf, BUFSIZ, fp) != (char *) NULL)
  588.         {
  589.             /* increment the number of lines in the article */
  590.  
  591.             total_lines++;
  592.  
  593.             /* Determine the type of line just read */
  594.  
  595.             switch(detline(buf))
  596.             {
  597.                 case DIGEST_TITLE:    /* A digest title line */
  598.                     /* Determine if this is the first article */
  599.                     if (first_art == 1)
  600.                     {
  601.                         /* This is the first article */
  602.  
  603.                         /* Record digest title line as article[0].subject */
  604.  
  605.                         (void) strncpy(articles[0].subject,buf,LINE_LEN - 1);
  606.  
  607.                         articles[0].subject[LINE_LEN - 1] = '\0';
  608.  
  609.                         /* Remove the trailing newline */
  610.  
  611.                         cptr = strchr(articles[art_count].subject, 
  612.                             '\n');
  613.  
  614.                         if (cptr != (char *) NULL)
  615.                             *cptr = '\0';
  616.  
  617.                         break;
  618.                     }
  619.                 case HYPHENS:         /* A hyphen line */
  620.                     /* Determine if this is the first article */
  621.                     if (first_art == 1)
  622.                     {
  623.                         /* This is the first article */
  624.  
  625.                         /* Determine the start of the article */
  626.  
  627.                         articles[0].offset = 0L;
  628.  
  629.                         /* Set first article to false */
  630.  
  631.                         first_art = 0;
  632.                     }
  633.  
  634.                     /* Update the article count */
  635.  
  636.                     art_count++; 
  637.  
  638.                     /* Update the num_lines in prev. article */
  639.  
  640.                     articles[art_count - 1].num_lines =
  641.                          total_lines - last_line;
  642.  
  643.             /* Attempt to add personal name to subject */
  644.  
  645.                     if (articles[art_count-1].subject[0] != '\0' &&
  646.             articles[art_count-1].from[0] != '\0')
  647.             {
  648.             (void) get_name(articles[art_count-1].from,name);
  649.             (void) strncat(articles[art_count-1].subject,name,
  650.                 LINE_LEN-1-strlen(articles[art_count-1].subject));
  651.             }
  652.             
  653.                     /* Update the last line */
  654.  
  655.                     last_line = total_lines;
  656.  
  657.                     /* Have we read the maximum number of articles? */
  658.  
  659.                     if (art_count < MAX_ARTICLES)
  660.                     {
  661.                        /* Determine the start of the next article */
  662.  
  663.                        articles[art_count].offset = ftell(fp);
  664.                     }
  665.                     else
  666.                     {
  667.                        /* Pass on the number of articles in digest */
  668.  
  669.                        *num_articles = MAX_ARTICLES;
  670.  
  671.                        (void) printf(
  672. "Warning! -- Digest contains more than %d articles, remainder ignored.\n",
  673.                               MAX_ARTICLES-1);
  674.  
  675.                        /* Sleep awhile to allow user to read msg */
  676.  
  677.                        sleep(11);
  678.  
  679.                        /* Exit the loop */
  680.   
  681.                        done = 1;
  682.                     }
  683.  
  684.                     break;
  685.  
  686.                 case FROM:            /* A From line */
  687.                     /* advance past the From: prefix */
  688.  
  689.                     cptr = strchr(buf, ':') + 1;
  690.              
  691.                     /* 
  692.                     -- Copy the contents of the from line, if there is 
  693.                     -- nothing already stored. I want only the first 
  694.                     -- occurance.
  695.                     */
  696.  
  697.                     if (articles[art_count].from[0] == '\0')
  698.                     {
  699.                         (void) strncpy(articles[art_count].from,
  700.                             cptr, LINE_LEN - 1);
  701.   
  702.                         articles[art_count].from[LINE_LEN - 1] = '\0';
  703.  
  704.                         /* Remove the trailing newline */
  705.  
  706.                         cptr = strchr(articles[art_count].from, '\n');
  707.                         if (cptr != (char *) NULL)
  708.                             *cptr = '\0';
  709.                     }
  710.  
  711.                     break;
  712.  
  713.                 case SUBJECT:         /* A Subject line */
  714.                     /* advance past the Subject: prefix */
  715.  
  716.                     cptr = strchr(buf, ':') + 1;
  717.              
  718.                     /* 
  719.                     -- Copy the contents of the subject line, if there 
  720.                     -- is nothing already stored. I want only the 
  721.                     -- first occurance.
  722.                     */
  723.  
  724.                     if (articles[art_count].subject[0] == '\0')
  725.                     {
  726.                         (void) strncpy(articles[art_count].subject, 
  727.                             cptr, LINE_LEN - 1);
  728.  
  729.                         articles[art_count].subject[LINE_LEN - 1] = 
  730.                             '\0';
  731.  
  732.                         /* Remove the trailing newline */
  733.  
  734.                         cptr = strchr(articles[art_count].subject, 
  735.                             '\n');
  736.  
  737.                         if (cptr != (char *) NULL)
  738.                             *cptr = '\0';
  739.                     }
  740.  
  741.                     /* Determine if this is the first article */
  742.                     if (first_art == 1)
  743.                     {
  744.                         /* This is the first article */
  745.  
  746.                         /* Attempt to retreive info from subject line */
  747.  
  748.                         cptr  = strchr(buf,':') + 2;
  749.                         cptr2 = strstr(cptr,"Digest V");
  750.                         if (cptr2 == (char *) NULL)
  751.                             cptr2 = strstr(cptr,"DIGEST V");
  752.  
  753.                         if (cptr2 != (char *) NULL)
  754.                         {
  755.                             /* Retreive the Digest Name */
  756.  
  757.                             cptr2 += strlen("Digest");
  758.                             len = cptr2 - cptr;
  759.                             (void) strncpy(digest,cptr,len);
  760.                             digest[len] = '\0';
  761.  
  762.                             /* Retreive the Issue Number */
  763.  
  764.                             cptr = strchr(cptr,'#');
  765.                             if (cptr != (char *) NULL)
  766.                             {
  767.                                 cptr++;
  768.                                 issue = atoi(cptr);
  769.                             }
  770.  
  771.                             /* Retreive the Volume Number */
  772.  
  773.                             cptr  -= 2;
  774.                             *cptr = '\0';
  775.                             cptr2 += 2;
  776.                             volume = atoi(cptr2);
  777.                         }
  778.                     }
  779.                             
  780.                     break;
  781.  
  782.                 default:         /* All other lines */
  783.                     break;
  784.             }
  785.         }
  786.         else
  787.         {
  788.             /* EOF or error */
  789.  
  790.             /* Determine if there was an error reading the file */
  791.  
  792.             if (ferror(fp))
  793.             {
  794.                 /* Error reading the file */
  795.  
  796.                 (void) sprintf(errmsg, 
  797.                     "%s (%d): Error reading file '%s'", __FILE__,
  798.                     __LINE__, fname);
  799.                 perror(errmsg);
  800.                 return_value = -1;
  801.             }
  802.             else
  803.             {
  804.                 /* EOF was reached. */
  805.  
  806.                 /* Pass on the number of articles in digest */
  807.  
  808.                 *num_articles = art_count;
  809.             }
  810.  
  811.             /* Exit the loop */
  812.   
  813.             done = 1;
  814.          }
  815.     }
  816.  
  817.     /* 
  818.     -- This is a somewhat feeble attempt to determine if the file was a 
  819.     -- valid digest. It assumes that at least 2 articles are found 
  820.     -- that fit the expected format and if found then it is a valid 
  821.     -- digest.
  822.     */
  823.  
  824.     if (return_value != -1 && art_count < 1)
  825.     {
  826.         /* The digest was an invalid file. */
  827.  
  828.         (void) fprintf(stderr, "%s (%d): Invalid Digest file '%s'.\n",
  829.             __FILE__, __LINE__, fname);
  830.         return_value = -1;
  831.     }
  832.  
  833.     return(return_value);
  834. }
  835.  
  836. /* Attempt to extract the personal name from the From: line */
  837.  
  838. void get_name(from,name)
  839.  
  840. char   *from;               /* From: line minus "From: " */
  841. char   *name;               /* Personal Name */
  842.  
  843. {
  844.     char   *cptr;           /* ptr into the given line */
  845.     char   *cptr2;          /* ptr into the given line */
  846.  
  847.     strcpy(name," (");
  848.     from++;
  849.  
  850.     if (*from == '\"')
  851.     {
  852.     from++;
  853.     if ( (cptr = strchr(from,'\"')) != (char *) NULL)
  854.     {
  855.         strncat(name,from,cptr-from);
  856.         strcat(name,")");
  857.     }
  858.     }
  859.     else
  860.     if ( (cptr = strstr(from," <")) != (char *) NULL)
  861.     {
  862.         strncat(name,from,cptr-from);
  863.         strcat(name,")");
  864.     }
  865.     else
  866.         if ( (cptr = strstr(from," (")) != (char *) NULL)
  867.         {
  868.         cptr += (sizeof(" (") - 1);
  869.         if ( (cptr2 = strchr(cptr,')')) != (char *) NULL)
  870.             strncat(name,cptr,cptr2-cptr+1); /* Includes ")" */
  871.         }
  872.  
  873.     if (strcmp(" (",name) == 0)
  874.     {
  875.     strcat(name,from);
  876.     strcat(name,")");
  877.     }
  878. }
  879.  
  880. /* Determine the type of line */
  881.  
  882. int detline(line)
  883. char   *line;               /* Line to evaluate */
  884. {
  885.     char   *cptr;           /* ptr into the given line */
  886.     int    i;               /* Loop control variable */
  887.     int    return_value;    /* Return value of function */
  888.     char   upper[BUFSIZ];   /* Upper case copies from line */
  889.  
  890.     /* convert line to upper case. */
  891.  
  892.     for (i = 0, cptr = line; *(cptr + i) != '\0' ; i++)
  893.         *(upper + i) = toupper(*(cptr + i));
  894.  
  895.     *(upper + i) = '\0';
  896.  
  897.     /* Determine the type of line it is */
  898.  
  899.     if (strncmp("FROM:", upper, 5) == 0)
  900.     {
  901.         /* It is a From line */
  902.  
  903.         return_value = FROM;
  904.     }
  905.     else if ( (strncmp("SUBJECT:", upper, 8) == 0) ||
  906.               (strncmp("SUBJ:", upper, 5) == 0) )
  907.     {
  908.         /* It is a Subject line */
  909.  
  910.         return_value = SUBJECT;
  911.     }
  912.     else if (strncmp("--------", upper, 8) == 0)
  913.     {
  914.         /* It is a hypen line */
  915.  
  916.         return_value = HYPHENS;
  917.     }
  918.     else if ( (strstr(upper," DIGEST ") != (char *) NULL) &&
  919.               (strstr(upper," VOLUME ") != (char *) NULL) &&
  920.               (strstr(upper," : ISSUE") != (char *) NULL) )
  921.     {
  922.         /* It is a digest title line */
  923.  
  924.         return_value = DIGEST_TITLE;
  925.  
  926.         /* Retrieve the Issue Number from the line */
  927.  
  928.     cptr = strstr(upper," : ISSUE") + strlen(" : ISSUE");
  929.     if (*cptr == ':')
  930.             cptr++;
  931.         issue = atoi(cptr);
  932.         
  933.         /* Retrieve the Volume Number from the line */
  934.  
  935.         cptr -= strlen(" : ISSUE");
  936.         *cptr = '\0';
  937.         volume = atoi(strstr(upper," VOLUME ") + strlen(" VOLUME "));
  938.  
  939.         /* Retrieve the Digest Name from the line */
  940.  
  941.         i = strstr(upper," DIGEST ") + strlen (" DIGEST") - &upper[0];
  942.         (void) strncpy(digest,line,i);
  943.         digest[i] = '\0';
  944.     }
  945.     else
  946.     {
  947.         /* It is an undetermined line type. */
  948.  
  949.         return_value = UNKNOWN;
  950.     }
  951.  
  952.     return(return_value);
  953. }
  954.  
  955. /* This function gets the filename from the user. */
  956.  
  957. int getfilename(fname,in_or_out)
  958. char    fname[];            /* File name retrieved from user */
  959. int     in_or_out;          /* INPUT or OUTPUT indicator */
  960. {
  961.     char   answer[MAX_FILE_LEN]; /* users response */
  962.     char   *front;          /* points to front of the file name */
  963.     char   *cptr;           /* used to NULL terminate file name */
  964.     int    done;            /* Loop control variable */
  965.     int    return_value;    /* Return value of the function */
  966.     
  967.     /* 
  968.     -- Retrieve the file name from the user. Loop until the user 
  969.     -- supplies a file name or q is selected.
  970.     */
  971.  
  972.     return_value = -1;
  973.     done = 0;
  974.     while (!done)
  975.     {
  976.         if (in_or_out == INPUT)
  977.         {
  978.             /* Clear the screen and prompt the user for a file name */
  979.  
  980.             clear();
  981.             (void) printf("\nEnter digest file name (q = quit): ");
  982.         }
  983.         else
  984.         {
  985.             /* Prompt the user for an output file name */
  986.  
  987.             (void) printf("\nEnter output file name (q = quit): ");
  988.         }
  989.  
  990.         /* Get the users response */
  991.  
  992.         if (fgets(answer, MAX_FILE_LEN, stdin) == (char *) NULL)
  993.         {
  994.             /* An Error or End of file was received. Exit the program */
  995.  
  996.             done = 1;
  997.         }
  998.         else
  999.         {
  1000.             /* Got a users response */
  1001.  
  1002.             /* Point to the first non-white space character */
  1003.  
  1004.             front = answer;
  1005.             while (isspace(*front))
  1006.                 front++;
  1007.  
  1008.             /* Determine an appropriate action based on the response. */
  1009.  
  1010.             if (*front == '\0')
  1011.             {
  1012.                 /* 
  1013.                 -- The user just hit return. Report to the user to 
  1014.                 -- use 'q' to quit or enter a file name.
  1015.                 */
  1016.  
  1017.                 (void) printf("\tEnter 'q' to quit or a file name.\n");
  1018.                 sleep(1);
  1019.             }
  1020.             else if (strcmp(front, "q\n") == 0)
  1021.             {
  1022.                 /* The user wants to quit. */
  1023.  
  1024.                 done = 1;
  1025.             }
  1026.             else
  1027.             {
  1028.                /* A file name was given */
  1029.  
  1030.                cptr = front;
  1031.  
  1032.                /* Remove the newline from the file name */
  1033.  
  1034.                while (!isspace(*cptr))
  1035.                    cptr++;
  1036.                *cptr = '\0';
  1037.  
  1038.                /* Copy the file name to fname */
  1039.  
  1040.                (void) strcpy(fname, front);
  1041.  
  1042.                /* Exit the loop and return to the calling function */
  1043.  
  1044.                done = 1;
  1045.                return_value = 0;
  1046.             }
  1047.         }
  1048.     }
  1049.  
  1050.     return(return_value);
  1051. }
  1052.  
  1053. /* Display a list of available articles */
  1054.  
  1055. int display_menu(articles, start, num_articles, total)
  1056. article     articles[];        /* Array listing available articles */
  1057. int         start;             /* First article to display */
  1058. int         num_articles;      /* num articles to display */
  1059. int         total;             /* total number of articles */
  1060. {
  1061.     int     i;                 /* Loop control variable */
  1062.     char    answer[MAX_ANS];   /* Users answer to prompt */
  1063.     char    *front;            /* Pointer to front of answer */
  1064.     int     return_value;      /* Function return value */
  1065.  
  1066.     /* Clear the screen */
  1067.  
  1068.     clear();
  1069.  
  1070.     /* Print out the list of articles */
  1071.  
  1072.     for (i = start; i < start + num_articles; i++)
  1073.     {
  1074.         if (articles[i].subject[0] != '\0')
  1075.             (void) printf("%02d> %.75s\n", i, articles[i].subject);
  1076.         else
  1077.             (void) printf("%02d> %.75s\n", i, articles[i].from);
  1078.     }
  1079.  
  1080.     /* 
  1081.     -- Print out blank lines to ensure that the prompt is at the 
  1082.     -- bottom of the page.
  1083.     */
  1084.  
  1085.     for (i = 0; i < xlines - PROMPT_LINES - num_articles; i++)
  1086.         (void) printf("\n");
  1087.  
  1088.     /* 
  1089.     -- Print out the menu prompt. The number of lines required should 
  1090.     -- be exactly equal to PROMPT_LINES in order for the program to 
  1091.     -- work correctly.
  1092.     */
  1093.  
  1094.     /* Prompt line 1 */
  1095.     (void) printf("\n");
  1096.  
  1097.     /* Prompt line 2 */
  1098.     (void) printf("--- (%s %d:%d, Articles (%d - %d) of %d) ---\n", 
  1099.         digest, volume, issue, start, start + num_articles - 1, total - 1);
  1100.  
  1101.     /* Prompt line 3 */
  1102.     (void) printf( "Article number, (n)ext, (p)revious, or (q)uit: ");
  1103.  
  1104.     /* Read in the users response */
  1105.  
  1106.     (void) fflush(stdin);
  1107.     if (fgets(answer, MAX_FILE_LEN, stdin) == (char *) NULL)
  1108.     {
  1109.         /* An Error or End of file was received. Exit the program */
  1110.  
  1111.         return_value = QUIT;
  1112.     }
  1113.     else
  1114.     {
  1115.         /* Got a users response */
  1116.  
  1117.         /* Point to the first non-white space character */
  1118.  
  1119.         front = answer;
  1120.         while (isspace(*front))
  1121.             front++;
  1122.  
  1123.         /* Determine an appropriate action based on the response. */
  1124.  
  1125.         if (isalpha(*front))
  1126.         {
  1127.             /* The user entered a letter response */
  1128.  
  1129.             switch (*front)
  1130.             {
  1131.                 case 'p':
  1132.                 case 'P':
  1133.                 case 'b':
  1134.                 case 'B':
  1135.                     return_value = PREVIOUS;
  1136.                     break;
  1137.  
  1138.                 case 'n':
  1139.                 case 'N':
  1140.                 case 'f':
  1141.                 case 'F':
  1142.                     return_value = NEXT;
  1143.                     break;
  1144.  
  1145.                 case 'q':
  1146.                 case 'Q':
  1147.                     return_value = QUIT;
  1148.                     break;
  1149.  
  1150.                 default:
  1151.                     clear();
  1152.                     (void) printf("Invalid request %s", front);
  1153.                     sleep(1);
  1154.                     return_value = ILLEAGAL;
  1155.                     break;
  1156.             }
  1157.         }
  1158.         else if (isdigit(*front))
  1159.         {
  1160.             /* The user entered an article number */
  1161.  
  1162.             return_value = atoi(front);
  1163.         }
  1164.         else
  1165.         {
  1166.             /* 
  1167.             -- The user entered something other than a letter or a 
  1168.             -- number
  1169.             */
  1170.  
  1171.             if (*front != '\0')
  1172.             {
  1173.                 clear();
  1174.                 (void) printf("Invalid request %s", front);
  1175.                 sleep(1);
  1176.                 return_value = ILLEAGAL;
  1177.             }
  1178.             else
  1179.             {
  1180.                 if ((start + (xlines - PROMPT_LINES)) <= (total - 1))
  1181.                     return_value = NEXT;
  1182.                 else
  1183.                     return_value = FIRST_PAGE;
  1184.             }
  1185.         }
  1186.     }
  1187.  
  1188.     return(return_value);
  1189. }
  1190.  
  1191. /* Display the article on the screen */
  1192.  
  1193. void display_article(fp, fname, article_num, articles, num_articles)
  1194. FILE        *fp;               /* input file */
  1195. char        *fname;            /* input file name */
  1196. int         article_num;       /* Index into articles, which article */
  1197. article     articles[];        /* Array of article information */
  1198. int         num_articles;      /* number of articles */
  1199. {
  1200.     int     total;             /* Total number of lines displayed */
  1201.     int     i;                 /* Loop control variable */
  1202.     int     count;             /* number of lines displayed on screen */
  1203.     int     len;               /* Length of a input line */
  1204.     int     inc;               /* Line count increment number */
  1205.     long    curr_line;         /* Offset to the current line */
  1206.     char    buf[BUFSIZ];       /* Input buffer */
  1207.     int     result;            /* return from function calls */
  1208.     int     done;              /* Loop control variable */
  1209.     int     prev_total;        /* previous totals */
  1210.     char    answer[MAX_ANS];   /* Users answer to prompt */
  1211.     char    *front;            /* Pointer to front of answer */
  1212.     long    curr_offset;       /* Current offset in file */
  1213.     void    extract_article();
  1214.  
  1215.     /* Seek to the articles location in the file */
  1216.  
  1217.     result = fseek(fp, articles[article_num].offset, 0);
  1218.  
  1219.     if (result == -1)
  1220.     {
  1221.         /* Error seeking in file */
  1222.  
  1223.         clear();
  1224.         (void) sprintf(errmsg, "%s (%d): Error seeking in file %s",
  1225.             __FILE__, __LINE__, fname);
  1226.         perror(errmsg);
  1227.     }
  1228.     else
  1229.     {
  1230.         /* 
  1231.         -- Since there was no error seeking in the file. I will assume 
  1232.         -- that all other I/O to the file will be good. (It just means 
  1233.         -- I was too lazy to check every call.)
  1234.         */
  1235.  
  1236.         /* Initialize local variables */
  1237.  
  1238.         total = 0;
  1239.  
  1240.         /* Display the article */
  1241.  
  1242.         done = 0;
  1243.         while (!done)
  1244.         {
  1245.             /* Display the article 'lines - 1' lines at a time */
  1246.  
  1247.             clear();
  1248.  
  1249.             /* Store the current position */
  1250.  
  1251.             curr_line = ftell(fp);
  1252.             prev_total = total;
  1253.  
  1254.             /* Display the article */
  1255.  
  1256.             for (count = 0; total < articles[article_num].num_lines &&
  1257.                  count < xlines - 2; count++, total++)
  1258.             {
  1259.                 curr_offset = ftell(fp);        /* Record current offset */
  1260.                 (void) fgets(buf, BUFSIZ, fp);
  1261.  
  1262.                 /* Determine if the line will wrap on the screen */
  1263.  
  1264.                 len = strlen(buf);
  1265.                 if (len > COLS)
  1266.                 {
  1267.                     /* 
  1268.                     -- The line will wrap on the screen. If the screen 
  1269.                     -- WRAPS versus TRUNCATE. If your screen TRUNCATES 
  1270.                     -- then set COLS to a large number.
  1271.                     */
  1272.  
  1273.                     /* Determine the number of lines to add to count */
  1274.  
  1275.                     inc = len / COLS;
  1276.  
  1277.                     /* 
  1278.                     -- Determine if the number of lines to add will 
  1279.                     -- bring us over the allowable lines.
  1280.                     */
  1281.  
  1282.                     if (count + inc >= xlines - 2)
  1283.                     {
  1284.                         /* The number of lines will be exceeded */
  1285.  
  1286.                         /* Back up one line in the input file */
  1287.  
  1288.                         (void) fseek(fp, curr_offset, 0);
  1289.                       
  1290.                         /* Fill the rest of the page with newlines */
  1291.  
  1292.                         for(; count < xlines - 2; count++)
  1293.                             (void) printf("\n");
  1294.                     }
  1295.                     else
  1296.                     {
  1297.                         /* 
  1298.                         -- The number of lines is below the number to 
  1299.                         -- fit on a screen.
  1300.                         */
  1301.              
  1302.                         count += inc;
  1303.                         (void) fputs(buf, stdout);
  1304.                     }
  1305.                 }
  1306.                 else
  1307.                 {
  1308.                     /* The line will not wrap */
  1309.  
  1310. #if !VAXC
  1311.                     (void) fputs(buf, stdout);
  1312. #else
  1313.                     if (len == 1 && count == 0)
  1314.                     {
  1315.                         /*
  1316.                         -- VMS eats a totally blank line after reading
  1317.                         -- input from terminal so output a line with a
  1318.                         -- space instead
  1319.                         */
  1320.                         (void) fputs(" \n", stdout);
  1321.                     }
  1322.                     else
  1323.                         (void) fputs(buf, stdout);
  1324. #endif
  1325.                 }
  1326.             }
  1327.  
  1328.             /* Print the prompt */
  1329.  
  1330.             if (total == articles[article_num].num_lines)
  1331.             {
  1332.                 for(i = count; i < xlines - 2; i++)
  1333.                     (void) printf("\n");
  1334.         if (article_num < num_articles - 1)
  1335.             i = article_num + 1;
  1336.         else
  1337.             i = 0;
  1338.         (void) printf("Next: %.73s\n", articles[i].subject);
  1339.                 (void) printf(
  1340. "--- #%d of %d (q)uit (t)op (n)ext (p)revious (e)xtract [ret - Next] (%d%%): ",
  1341.                     article_num, num_articles - 1, 100);
  1342.             }
  1343.             else
  1344.             {
  1345.         (void) printf("\n");
  1346.                 (void) printf(
  1347. "--- #%d of %d (q)uit (t)op (n)ext (p)revious (e)xtract [ret - More] (%d%%): ",
  1348.                     article_num, num_articles - 1,
  1349.                     total * 100 / articles[article_num].num_lines);
  1350.             }
  1351.  
  1352.             /* Clear out the input buffer */
  1353.  
  1354.             (void) fflush(stdin);
  1355.  
  1356.             /* Get the users response */
  1357.  
  1358.             if (fgets(answer, MAX_FILE_LEN, stdin) == (char *) NULL)
  1359.             {
  1360.                 /* An Error or End of file was received. */
  1361.  
  1362.                 done = 1;
  1363.             }
  1364.             else
  1365.             {
  1366.                 /* Got a users response */
  1367.  
  1368.                 /* Point to the first non-white space character */
  1369.  
  1370.                 front = answer;
  1371.                 while (isspace(*front))
  1372.                     front++;
  1373.  
  1374.                 /* 
  1375.                 -- Determine an appropriate action based on the 
  1376.                 -- response. 
  1377.                 */
  1378.  
  1379.                 switch (*front)
  1380.                 {
  1381.                     case 'Q':      /* Quit reading article */
  1382.                     case 'q':
  1383.                         done = 1;  /* Exit the loop */
  1384.                         break;
  1385.  
  1386.                     case 'p':      /* Previous Article */
  1387.                     case 'P':
  1388.                 /* 
  1389.                 -- Go to the previous article and prepare to 
  1390.                 -- print from the first line.
  1391.                 */
  1392.  
  1393.                 total = 0;
  1394.                 article_num--;
  1395.                 if (article_num < 0)
  1396.                 article_num = num_articles - 1;
  1397.  
  1398.                 /* Goto the first line of the article */
  1399.  
  1400.                 (void) fseek(fp, 
  1401.                 articles[article_num].offset, 0);
  1402.  
  1403.                         break;
  1404.  
  1405.                     case 't':      /* Top of article */
  1406.                     case 'T':
  1407.                         /* Goto the first line of the article */
  1408.  
  1409.                         (void) fseek(fp, articles[article_num].offset, 
  1410.                             0);
  1411.  
  1412.                         /* Set to print the first line */
  1413.  
  1414.                         total = 0;
  1415.                         break;
  1416.  
  1417.                     case '\0':     /* Return */
  1418.                         /* 
  1419.                         -- Determine if the whole article has been 
  1420.                         -- displayed. If the whole article was not 
  1421.                         -- diplayed, then return will show more of the 
  1422.                         -- article.
  1423.                         */
  1424.  
  1425.                         if (total < articles[article_num].num_lines)
  1426.                             break;
  1427.  
  1428.                         /*
  1429.                         -- If we get here then the whole article was
  1430.                         -- displayed so fall through to Next Article
  1431.                         -- code to display the next article.
  1432.                         */
  1433.  
  1434.                     case 'n':      /* Next Article */
  1435.                     case 'N':
  1436.                         /* 
  1437.                         -- Advance to the next article and set to
  1438.                         -- print on the first line.
  1439.                         */
  1440.  
  1441.                         total = 0;
  1442.                         article_num++;
  1443.  
  1444.                         if (article_num > num_articles - 1)
  1445.                             article_num = 0;
  1446.  
  1447.                         /* Goto the first line of the article */
  1448.  
  1449.                         (void) fseek(fp, 
  1450.                             articles[article_num].offset, 0);
  1451.  
  1452.                         break;
  1453.  
  1454.                     case 'e':      /* Extract article to a file */
  1455.                     case 'E':
  1456.                         (void) extract_article(fp, fname, article_num,
  1457.                             articles);
  1458.                         (void) fseek(fp, curr_line, 0);
  1459.                         total = prev_total;
  1460.                         break;
  1461.  
  1462.                     default:
  1463.                         clear();
  1464.                         (void) printf("Invalid response %s", front);
  1465.                         sleep(1);
  1466.                         (void) fseek(fp, curr_line, 0);
  1467.                         total = prev_total;
  1468.                         break;
  1469.                 }
  1470.             }
  1471.         }   /* End while not done */
  1472.     }
  1473. }
  1474.  
  1475. /* Extract the article to a file */
  1476.  
  1477. void extract_article(fp, fname, article_num, articles)
  1478. FILE        *fp;               /* input file */
  1479. char        *fname;            /* input file name */
  1480. int         article_num;       /* Index into articles, which article */
  1481. article     articles[];        /* Array of article information */
  1482. {
  1483.     int     count;                 /* number of lines extracted */
  1484.     char    buf[BUFSIZ];           /* Input buffer */
  1485.     int     result;                /* return from function calls */
  1486.     FILE    *ofp;                  /* output file */
  1487.     char    ofname[MAX_FILE_LEN];  /* output file name */
  1488.     char    full_name[BUFSIZ];     /* full file name of output file */
  1489.  
  1490.     /* Get the name of the output file from the user */
  1491.  
  1492.     result = getfilename(ofname,OUTPUT);
  1493.  
  1494.     /* Determine if the user selected quit */
  1495.  
  1496.     if (result == -1) return;
  1497.  
  1498.     /* Attempt to open the output file */
  1499.  
  1500.     ofp = fopen(ofname, "w");
  1501.  
  1502.     /* Determine if there was an error opening the file */
  1503.  
  1504.     if (ofp == (FILE *) NULL)
  1505.     {
  1506.         /* 
  1507.         -- There was an error opening the digest file, report the 
  1508.         -- error and exit the function.
  1509.         */
  1510.  
  1511.         (void) sprintf(errmsg, "%s (%d): Error opening file '%s'", 
  1512.             __FILE__, __LINE__, ofname);
  1513.         (void) perror(errmsg);
  1514.     }
  1515.     else
  1516.     {
  1517.         /* The output file was successfully opened. */
  1518.  
  1519. #if VAXC
  1520.         /* In VAXC use fgetname to get the full name of the output file */
  1521.  
  1522.         (void) fgetname(ofp,full_name);
  1523. #else
  1524.         /* If not VAX simply use "ofname" as the full name */
  1525.  
  1526.         (void) strcpy(full_name,ofname);
  1527. #endif
  1528.  
  1529.         /* Seek to the articles location in the file */
  1530.  
  1531.         result = fseek(fp, articles[article_num].offset, 0);
  1532.  
  1533.         if (result == -1)
  1534.         {
  1535.             /* Error seeking in file */
  1536.  
  1537.             clear();
  1538.             (void) sprintf(errmsg, "%s (%d): Error seeking in file %s",
  1539.                 __FILE__, __LINE__, fname);
  1540.             perror(errmsg);
  1541.         }
  1542.         else
  1543.         {
  1544.             /* 
  1545.             -- Since there was no error seeking in the file. I will assume 
  1546.             -- that all other I/O to the file will be good. (It just means 
  1547.             -- I was too lazy to check every call.)
  1548.             */
  1549.  
  1550.             /* Extract the article */
  1551.  
  1552.             for (count = 0; count < articles[article_num].num_lines; count++)
  1553.             {
  1554.                 (void) fgets(buf, BUFSIZ, fp);
  1555.                 (void) fputs(buf, ofp);
  1556.             }
  1557.  
  1558.             (void) printf("Article #%d (%d lines) extracted to file '%s'\n",
  1559.                 article_num,articles[article_num].num_lines,full_name);
  1560.  
  1561.             sleep(1);
  1562.         }
  1563.  
  1564.         /* Close the open file */
  1565.  
  1566.         (void) fclose(ofp);
  1567.     }
  1568. }
  1569.  
  1570. #if VAXC
  1571. /*----------------------------------------------------------------------*/
  1572. /* Find_File - Subroutine to call LIB$FIND_FILE                */
  1573. /*----------------------------------------------------------------------*/
  1574.  
  1575. #include descrip
  1576.  
  1577. #define MULTIPLE 2
  1578.  
  1579. unsigned long find_file(fspec,fname,fname_len)
  1580.  
  1581. char    *fspec;
  1582. char    *fname;
  1583. long    fname_len;
  1584.  
  1585. {
  1586.     static int    context=0;
  1587.  
  1588.     register unsigned long    succ;
  1589.     register char        *ptr;
  1590.  
  1591.     struct dsc$descriptor_s fspec_d = 
  1592.         {strlen(fspec), DSC$K_DTYPE_T, DSC$K_CLASS_S, fspec};
  1593.     struct dsc$descriptor_s fname_d = 
  1594.         {fname_len, DSC$K_DTYPE_T, DSC$K_CLASS_S, fname};
  1595.  
  1596.     succ = LIB$FIND_FILE(&fspec_d,&fname_d,&context,0,0,0,&MULTIPLE);
  1597.  
  1598.     fname[fname_len-1] = ' ';
  1599.  
  1600.     for(ptr=fname; *ptr != ' '; ptr++)
  1601.         ;
  1602.     *ptr = '\0';
  1603.  
  1604.     return succ;
  1605. }
  1606. #endif
  1607.  
  1608.  
  1609.  
  1610.